#include <sys/ioctl.h>

#include "SpiChannel.h"


static const  int SPI_DATA_READY = SIGRTMIN + 3;

DummyChannel::Mutex SpiChannel::mallChannelMutex;

sigset_t SpiChannel::sigset;
siginfo_t SpiChannel::siginfo;

BasicDevice SpiChannel::msignalDevice("/dev/stm32_0");

void SpiChannel::initSignalEnv() {
    sigemptyset(&sigset);
    sigaddset(&sigset, SPI_DATA_READY);
//   sigaddset(&sigset, SIGIO);
    sigaddset(&sigset, SIGUSR1);
    pthread_sigmask(SIG_SETMASK, &sigset, nullptr);

    fcntl(msignalDevice.getDeviceFd(), F_SETOWN, getpid());
    int oflags = fcntl(msignalDevice.getDeviceFd(), F_GETFL);

    fcntl(msignalDevice.getDeviceFd(), F_SETSIG, SPI_DATA_READY);

    fcntl(msignalDevice.getDeviceFd(), F_SETFL, oflags | FASYNC);

//    ioctl(msignalDevice.getDeviceFd(), 1);
}



DeviceInfoHeader_t* SpiChannel::waitAndRead(const std::string& exceptionTip, const timespec* timeout, DeviceCommunicator& communicator, void* buffer, ssize_t& length) {
    int32_t channel = sigtimedwait(&sigset, &siginfo, timeout);
    if (0 > channel) {
        std::string message{__FUNCTION__};
        LOG_ERROR(buildErrorMessage(channel, errno, message).data());
        return nullptr;
    }

    LOG_DEDUG("received signal:%d", channel);
//    std::string recvBuffer;
//    recvBuffer.resize(HEADER_LENGTH);
    std::unique_lock<DummyChannel::Mutex> locker(mallChannelMutex);

    ssize_t readed = communicator.read(exceptionTip, buffer, length);
    locker.unlock();
    DeviceInfoHeader_t* pdeviceHeader = nullptr;
    if (HEADER_LENGTH + DATA_OFFSET <= readed) {
        char* pBuffer = reinterpret_cast <char*>(buffer);// + DATA_OFFSET
        std::string_view view{pBuffer, static_cast <std::string_view::size_type>(readed)};
        std::string_view::size_type index = view.find("@@");
        if (std::string_view::npos != index) {
            pBuffer += index;
            LightweightHeader_t* pheader = reinterpret_cast <LightweightHeader_t*>(pBuffer);
            length = pheader->length;
            pdeviceHeader = reinterpret_cast <DeviceInfoHeader_t*>(pheader->payload);
            channel = pdeviceHeader->channel;
        }
    }
    return pdeviceHeader;
}



std::string& SpiChannel::sendStringReturnString(const std::string& parameter, std::string& result) {
    LOG_DEDUG("%s,sendStringReturnString:parameter:%s", mexceptionTip.data(), parameter.data());
    using namespace std::chrono_literals;
    std::unique_lock<Mutex> channelLocker(mmutex);

    std::unique_lock<Mutex> locker(mallChannelMutex);
    ssize_t length = write(parameter);
    locker.unlock();

    mpcurrentResult = &result;
    const std::cv_status waitStatus = mchannelCondition.wait_for(channelLocker, 200ms);
    mpcurrentResult = nullptr;
    channelLocker.unlock();
    if(std::cv_status::no_timeout == waitStatus) {
        LOG_DEDUG("%s,sendStringReturnString:result:%s", mexceptionTip.data(), result.data());
    } else {
        throw BUILD_EXCEPTION_MESSAGE(FirmwareError::CHANNEL_COMM_ERROR, "wait for device response timeout!");
    }

    return result;
}


bool SpiChannel::feedbackData(DeviceInfoHeader_t* pdeviceHeader, size_t length) {

    std::unique_lock<Mutex> channelLocker(mmutex);
    if (mpcurrentResult) {
        mpcurrentResult->assign(reinterpret_cast<typename std::string::const_pointer>(pdeviceHeader->payload), length - sizeof (DeviceInfoHeader_t));
    }
    mchannelCondition.notify_one();

    return true;
}



